package joodle.tools;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;

import joodle.domain.Canvas;
import joodle.domain.Point;
import joodle.domain.Shape;

/**
 * This class is an abstraction for any kind of affine transformation on a shape. An affine 
 * transformation, maps parallel lines to parallel lines and hence can be represented by a matrix of 
 * constant real numbers. This class provides the boilerplate functionality for Affine transforms 
 * which is  a default implementation for the handlePress and handleRelease methods. Different 
 * transformations should subclass TransformationTool and provide their own implementation for the 
 * handleMove method.
 * <p><center>
 * <img src="http://ug.bcc.bilkent.edu.tr/~m_ozdal/Site/Reports_files/design_files/image011.jpg" />
 * </center></p>
 */
public abstract class TransformationTool implements Tool {
	static final Color  INTERIM_COLOR = new Color(128, 128, 128);//Color.lightGray;
	static final Stroke INTERIM_STROKE = new BasicStroke(
			1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND,
			0, new float[] { 2.0f, 2.0f }, 0);

	protected Point firstPoint;
	protected Point currentPoint;
	protected Shape shape;
	protected Shape transformedShape;
	protected AffineTransform currentTransform;
	
	public TransformationTool() {
		currentTransform = new AffineTransform();
	}

	/**
	 * Draws an intermediate figure which consists of a transformed shape
	 * @see Tool#drawInterim(Graphics)
	 */
	@Override
	public void drawInterim(Graphics g) {
		if(transformedShape == null)
			return;
		
		transformedShape.draw(g);
	}

	/**
	 * Invoked when the mouse button has been clicked (pressed and released) on a component.
	 * @see Tool#handlePress(Point, boolean)
	 */
	@Override
	public void handlePress(Point p, boolean ctrl) {
		shape = Canvas.instance().getShapeByPoint(p);
		if(shape == null) {
			currentPoint = null;
			transformedShape = null;
		}
		else {
			currentPoint = p;
			transformedShape = shape.cloneShape();
			transformedShape.setAttributeValue(Shape.Attribute.COLOR, INTERIM_COLOR);
			transformedShape.setAttributeValue(Shape.Attribute.LINE_STROKE, INTERIM_STROKE);
			if(firstPoint == null) {
				firstPoint = p;
			}
		}
	}

	/**
	 * Invoked when a mouse button has been released on a component. Updates the component according
	 * to current transformations
	 * @see Tool#handleRelease(Point, boolean)
	 */
	@Override
	public void handleRelease(Point p, boolean ctrl) {
		if(shape == null)
			return;
		
		shape.transform(currentTransform);
		reset();
	}

	/**
	 * Invoked when escape key has been pressed. If escape key is pressed, it also resets the changes 
	 * on the specified component
	 * @see Tool#handleKeyPress(int)
	 */
	@Override
	public void handleKeyPress(int c) {
		if(c == KeyEvent.VK_ESCAPE)
			reset();
	}
	
	/**
	 * @see Tool#handleKeyType(char)
	 */
	@Override
	public void handleKeyType(char c) {
	}

	/**
	 * Resets the changes on the specified component
	 */
	private void reset() {
		currentTransform.setToIdentity();
		shape = transformedShape = null;
		currentPoint = firstPoint = null;
	}
}
